package naga.x.util;

import java.io.File;
import java.io.FileFilter;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassUtils {

	private ClassUtils() {}
	
	public static List<Class<?>> scanPackage(String packageName, boolean recursive) {
		try {
			URL url = Thread.currentThread().getContextClassLoader().getResource(packageName.replace('.', '/'));
			String protocol = url.getProtocol();
			if ("file".equals(protocol)) {
				return scanPackageInFile(new File(CommonUtils.decodeURL(url.getFile(), IOUtils.UTF8)), packageName, recursive);
			}
			if ("jar".equals(protocol)) {
				JarURLConnection conn = (JarURLConnection) url.openConnection();
				return scanPackageInJar(conn.getJarFile(), packageName, recursive);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return Collections.emptyList();
	}

	public static List<Class<?>> scanPackageInFile(File file, String packageName, boolean recursive) {
		if (file.isDirectory()) {
			return scanPackageInFile(file, packageName, recursive, new ArrayList<Class<?>>());
		}
		return Collections.emptyList();
	}
	
	private static List<Class<?>> scanPackageInFile(final File file, final String packageName, final boolean recursive, final List<Class<?>> classHolder) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		file.listFiles(new FileFilter() {
			@Override
			public boolean accept(File f) {
				if (recursive && f.isDirectory()) {
					scanPackageInFile(f, packageName + '.' + f.getName(), recursive, classHolder);
				} else if (f.isFile() && f.getName().endsWith(".class"))  {
					String className = packageName + '.' + f.getName().substring(0, f.getName().length() - 6);
					try {
						classHolder.add(classLoader.loadClass(className));
					} catch (ClassNotFoundException e) {
						e.printStackTrace();
					}
				}
				return false;
			}
		});
		return classHolder;
	}
	
	public static List<Class<?>> scanPackageInJar(JarFile jar, String packageName, boolean recursive) {
		List<Class<?>> classes = new ArrayList<Class<?>>();
		packageName = packageName.replace('.', '/');
		if (packageName.charAt(packageName.length() - 1) != '/') {
			packageName += '/';
		}
		Enumeration<JarEntry> enu = jar.entries();
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		while (enu.hasMoreElements()) {
			JarEntry e = enu.nextElement();
			String name = e.getName();
			if (!e.isDirectory() && name.endsWith(".class") && name.startsWith(packageName)) {
				if (recursive || name.substring(packageName.length()).indexOf('/') < 0) {
					try {
						name = name.substring(0, name.length() - 6/*".class".length()*/).replace('/', '.');
						classes.add(classLoader.loadClass(name));
					} catch (ClassNotFoundException ex) {
						ex.printStackTrace();
					}
				}
			}
		}
		return classes;
	}
	
}
